package com.acxy.ants.core.kit;

import com.acxy.ants.common.kit.JsonMap;
import com.acxy.ants.common.kit.PathKit;
import com.acxy.ants.core.annotation.Application;
import com.acxy.ants.core.config.Modules;

import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.lang.annotation.Annotation;
import java.net.URL;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;

/**
 * 扫描注解工具类
 * 获取注解信息
 *
 * @author MrShun
 * @version 1.0
 * @Date 2017-04-26
 */
public class ScanKit {


    /**
     * 获取所有Class注解类对应的Class
     *
     * @param annotation 注解类
     * @return
     */
    public static List<Class<?>> getAnnotatedClass(Class<? extends Annotation> annotation) {
        return getAnnotatedClass(null, annotation);
    }

    /**
     * 根据包名称获取注解类对应的Class
     *
     * @param packageNames 包名称
     * @param annotation   注解类
     * @return
     */
    public static List<Class<?>> getAnnotatedClass(String packageNames, Class<? extends Annotation> annotation) {
        List<Class<?>> classList = new ArrayList<>();
        List<String> list = new ArrayList<>();
        if (packageNames == null) {
            //正常war运行模式
            URL resource = Thread.currentThread().getContextClassLoader().getResource("");
            if (resource == null) {
                LogKit.info("=== 目前处于jar运行模式 ===");
                String jarPath = PathKit.getJarPath();
                Modules.initModules(jarPath, annotation, classList);
                return classList;
            } else {
                LogKit.info("=== 目前处于war运行模式 ===");
                if (annotation == Application.class) {
                    //扫描当前子模块
                    String path = PathKit.getClassPath();
                    loadPathClass(path, list);
                } else {
                    String[] scans = (String[]) app.get("scans");
                    if (scans == null || scans.length > 0) {
                        //扫描全工程
                        String path = PathKit.ProjectPath();
                        loadPathClass(path, list);
                    } else if(scans.length == 0){
                        //扫描子模块
                        String path = PathKit.getClassPath();
                        loadPathClass(path, list);
                    }
                }

            }

        } else {
            String[] packages = packageNames.split(",");
            for (String packageName : packages) {
                String packageDirName = packageName.replace(" ", "").replace('.', File.separatorChar);
                Enumeration<URL> dirs = null;
                //获取当前目录下面的所有的子目录的url
                try {
                    dirs = Thread.currentThread().getContextClassLoader().getResources(packageDirName);
                } catch (IOException e) {
                    e.printStackTrace();
                }
                while (dirs.hasMoreElements()) {
                    URL url = dirs.nextElement();
                    //得到当前url的类型
                    String protocol = url.getProtocol();
                    //如果当前类型是文件类型
                    if ("file".equals(protocol)) {
                        //获取包的物理路径
                        String filePath = null;
                        try {
                            filePath = URLDecoder.decode(url.getFile(), "UTF-8").substring(1);
                            loadPathClass(filePath, list);
                        } catch (UnsupportedEncodingException e) {
                            e.printStackTrace();
                        }

                    }
                }
            }
        }
        for (String className : list) {
            try {
                Class<?> cls = Thread.currentThread().getContextClassLoader().loadClass(className);
                if (cls != null && cls.getAnnotation(annotation) != null && !cls.isInterface()) {
                    classList.add(cls);
                }
            } catch (ClassNotFoundException e) {
                LogKit.info(className + " load class is error!");
                continue;
            }
        }
        return classList;
    }


    /**
     * 迭代目录下面class入到集合中
     *
     * @param filePath 迭代的文件目录
     * @param packages 接收集合
     */
    private static void loadPathClass(String filePath, List<String> packages) {
        File dir = new File(filePath);
        File[] files = dir.listFiles(); // 该文件目录下文件全部放入数组
        if (files != null) {
            for (File file : files) {
                String path = file.getPath();
                boolean check;
                if (app == null || app.size() == 0) check = true;
                else {
                    String[] scans = (String[]) app.get("scans");
                    check = existStr(scans, path);
                }
                if (!file.isDirectory() && path.endsWith(".class") && check) {
                    String className = path.split("classes\\" + File.separator)[1].replace(File.separator, ".");
                    if (className.endsWith(".class")) {
                        packages.add(className.substring(0, className.lastIndexOf(".")));
                    }
                } else
                    loadPathClass(path, packages);
            }

        }
    }

    private static final JsonMap app = JsonMap.newJsonMap();


    /**
     * 获取全局启动配置文件
     *
     * @return
     */
    public static JsonMap getApplicationConfig() {
        List<Class<?>> apps = getAnnotatedClass(Application.class);
        if (apps == null || apps.size() == 0) return null;
        else {
            Class cls = apps.get(0);
            app.put("configClass", cls.getName());
            Annotation anno = cls.getAnnotation(Application.class);
            if (anno != null) {
                Application application = (Application) anno;
                app.put("modules", application.value());
                String[] scans = application.scan();
                if (scans.length == 0) {
                    int lastNum = cls.getName().lastIndexOf(".");
                    if (lastNum == -1) throw new RuntimeException(cls.getName() + " 运行主类必须带包名!");
                    app.put("scans", new String[]{cls.getName().substring(0, lastNum)});
                } else
                    app.put("scans", scans);
            }
            return app;
        }
    }

    /**
     * 判断字符串数组里面是否存在某个字符串
     *
     * @param sts 字符串素组
     * @param s   判断字符
     * @return
     */
    private static boolean existStr(String[] sts, String s) {
        for (String st : sts) {
            st = st.replace(".", "\\");
            if (s.indexOf(st) != -1) {
                return true;
            }
        }
        return false;
    }
}
